package com.caishi.lkx.common.config;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.crypto.symmetric.AES;
import com.alibaba.fastjson.JSON;
import com.zzw.common.context.ThreadCacheContext;
import com.zzw.common.exception.BizRuntimeException;
import com.zzw.common.resolver.KerayHandlerMethodArgumentResolver;
import com.zzw.common.resolver.KerayHandlerMethodArgumentResolverConfig;
import com.caishi.lkx.common.AclResultCode;
import com.caishi.lkx.common.EncryptionApi;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.util.encoders.Base64;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.*;

@Configuration
@Slf4j
public class EncryptionKerayHandlerMethodArgumentResolverConfig implements KerayHandlerMethodArgumentResolver, ThreadCacheContext {

    private static final String ALGORITHMS = "AES/ECB/PKCS5Padding";
    private final KerayHandlerMethodArgumentResolverConfig resolverConfig;
    private final RequestResponseBodyMethodProcessor bodyMethodProcessor;

    public static final String privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDYHJJPaLzLzcnZzRzu1t/1PwJB9BGrK84lbuZTLYlKXrdzcOzzjRXXI4o//Rs8tE7Na8zXUtGlIkWrrGN+k0gSps5kZ6jCeanzNlMVuMIikrcBBTQfJ5lSzRtVqaAnRq43EHj85mw74zInzTeg3sxxDzNqd14CMSkSjo8HgOljL0MPe+OR7HNZgWaOt5U/hLoyEOGAiSOAmDIIcv/X+JQoPKHf5aP/G+A6C3VfcBJcH1eqzM2EezO39PwmgTHzh6540UCRpP1+LcY3zPjOeg9cTY/EcsDE1B1bedE+C3rECaq8W1SdpQBGxhjoK2GoZy4fYSjdG624xcZZaES+G9DhAgMBAAECggEADVnqh2xi6ZEQ94Xj/ooY3k0NEH0J0GeNk04gKE2yYyf/MRXDayKGvGVY3QN/43Fc7Z+g0UFabAIg3W5yMSIxPDHA+NQB9Dh8UZHEHUiY+ct9nIWxRtF1TU0YSLkI3hRQSVzuVJV5uf4bHaQLMULtIiOnG1ySMBQlw5yPZTkcx2tJfsCls0qfhU6q/m7AyOC1YcjPV6feG7rGWjEVRL6ir7MLA1a6TI5gVLKD6gwx9wjRxVbP3Xipx8UCYHfzHUnaqdiurxKkYJM3WEQn8e3YRltiNOSz3pWNzKtD3m9bFumenJgaiJfiBhdh7paHWrnftTFLd2PgVRWg9r3aZma0bwKBgQDmVJ3rVzLWUFwygkCJQ3+zyG9bierlefwxBkTs4sX8aRigITWhdN8+vC09H/z5TaVUHPfeuk1TrQC7DEgw1itwJecupQLSGR+7VYgeVJIdixkf5h7OLNwrYvpcqfaENH+ZWq7iQnYYXHgBIG4jf3iG7OBVf+Vh0UxWbGNE922X1wKBgQDwMkkPV9ebH0jIHKrb6YwC2elyoNYA2LPgvkQ87lqe2WMK1guuk4J+lcndHyTHTybJUkwa1M9tl/oCZGEN2cLNbZBuht5Kw2KgLVIWGv4tbbSV9NfFqZfLDN0gGoWvzVIu6qd5qDitg+RaXHxdKDubGyE29+DBgsvvr7iO1atmBwKBgH5+ihvtT6YTjA7RKdA4mbGKql2DaNWOIMprvv65rDHrJfMLSgDZxqEVh2LCZpQj6GBBaH+RoVYK9RPEswDfHgwBRO2BAgOQeLTRTGgb2gMPmUTDhLeGO4Q4X3b0gMYqbskx3Em2ZlUKs30q6lHkd7Th8nICQ19tKpdr/zipCi8RAoGBAM8VK3P08celYyRTwuKyKanTobbX/2jfa8WZhE9b1/kXxpHzFENAncLw9QaU8Sp8jR4Iu+BuFR+RZVRmol5EnfRqP5lq4X9nkEI0kI7KWtVjvcuDOfwKSPogbwxckWGODQqx+A2lAKhoC5n0HqxaIXmKHEq9zqJHV/efceiDQrBFAoGBAK4/hNWifOPyfp4UkIpSdaX9jZeIxUc8ttGlfuji3T5w4eYKD9AYKAeAdZtCq3OZS2/M5MVDwy/6QjeOSniwS+QbpYHdI/u4AihvQgQ3IFXKsGGpKtRn2O9MUYYZnKMTVOFomL/2cNwR4h7LMORq1RjLfGQgStrJU/DMOV/inxyf";

    public static final String publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2ByST2i8y83J2c0c7tbf9T8CQfQRqyvOJW7mUy2JSl63c3Ds840V1yOKP/0bPLROzWvM11LRpSJFq6xjfpNIEqbOZGeownmp8zZTFbjCIpK3AQU0HyeZUs0bVamgJ0auNxB4/OZsO+MyJ803oN7McQ8zandeAjEpEo6PB4DpYy9DD3vjkexzWYFmjreVP4S6MhDhgIkjgJgyCHL/1/iUKDyh3+Wj/xvgOgt1X3ASXB9XqszNhHszt/T8JoEx84eueNFAkaT9fi3GN8z4znoPXE2PxHLAxNQdW3nRPgt6xAmqvFtUnaUARsYY6CthqGcuH2Eo3RutuMXGWWhEvhvQ4QIDAQAB";


    private static final String sm2PrivateKey = "78b774531abc57be5887016f07efa707c76328189c6dfd69f9c1b74cf0b14b93";

    /**
     * 提供给前端
     */
    private static final String sm2PublicKey = "04adb20f3657a551b171e769567458b11944188f17e2669cfb9cf16d9b625657cfe9db5d48cc0c0971a51f8d4b585012760b54f347cbf23e01d704236444d1607d";

    private static final String aesKey = "9b91f1963cf54dde82293db9295bb89b";

    private static final SM2 sm2;
    public static final RSA rsa;
    public static final AES aes;


    static {
        sm2 = new SM2(sm2PrivateKey, sm2PublicKey);
        sm2.setMode(SM2Engine.Mode.C1C2C3);
        rsa = new RSA("RSA/ECB/PKCS1Padding", privateKey, publicKey);
        aes = new AES(Mode.ECB, Padding.PKCS5Padding, aesKey.getBytes(StandardCharsets.UTF_8));
    }

    public static void main(String[] args) throws NoSuchAlgorithmException {
        EncryptionKerayHandlerMethodArgumentResolverConfig.test();
    }

    public static void test() throws NoSuchAlgorithmException {


        String str = "{\"industryCode\":\"yiyao\",\"examId\":\"9\",\"categoryId\":22,\"collectId\":\"1819214850212958208\",\"qid\":\"57_1819263793743466496\",\"type\":\"fill\",\"as\":\"[\\\"sdfffffffffffffffffffffffffff\\\"]\",\"answer\":\"Ps7tk7lupsEFhKEotrTGjmc2N2pXGeCasrp66AevExdfK95v7cnyjJxurJUPpMR5CYb7oueRdMA4+hZgixBZvT9m+fVNHBEURdQJSfv/ppwC69E1y2eKlD83dtsfJ8jF\"}";
        var str1 = aes.encryptBase64(str, CharsetUtil.UTF_8);
        //var  rsajia = HexUtil.encodeHexStr(str1);
        System.out.println("Encrypted message: " + str1);

        str1 = "6OZ27uuSPutDJsfG+ki3i370QqvmLUknt2fOMk9ez2sdH1SM0gDl9hsOKwhASgyzLEwW3rj9v7NHGElDgkqoA20AZ3t194TFINyZ5CL+tHUpQVeXlxm7gCe7rQ8AGrefFx6GvojbFjI8mqDruBF4IdIiAxYcs8ylwo9DyJ1TnOE3agC9Wh9Q2rCyvlhrE8al8WjYXgxA1ET1VzEQEX9fj5wS/7mgWuE0dfT+n0hshSofU7huckifP/jrSMFdnhbXtM14izvu9bJb8+NXyWYUgXsTz7zkIWickgAoP+aFqK4JyQJm8cev9f4FSHVSFiC8";
        var jiemi = aes.decryptStr(str1);

        System.out.println("Encrypted message 1123: " + jiemi);

        byte[] b = sm2.encrypt(str, KeyType.PublicKey);

        var hi = new String(Base64.decode(b));

        //var syr = new String(Base64.decode(sm2.decrypt("6d51627e70f1596ed21c0ab16c0327c09d34c1bb80ddbbebc1a795a74e27c09e21d9dffa63123d15eee442eb0af0cad1953f24c8148558e4ffb61a0aa57d81e8783b7ec6b387b6895cceed8480809ac994e2841b7b98f8c0e16d51fbfae3aecc9a48b81d50d11ae25154711bc796c19b063a8af37b8758621310f9b7402efb0bb2a9cffb504ac26767a593d6d3fb16377f5341f4441f99e340edff293b74a8d4a7f3d9e21e0fd9d3202be4c27b4a4ed7a5f48c2327bac14e501cd8f353b66890bd0705aa34eda05a1080e4991f975cc82db8c2881a22a74e84bd30bdc392f966838c4aabb2850c7f3d0a36e38a10de06b09bfbdda787966b5ada7dc232eb1238e229c09005d385f722d26eab9e2bded4370bac2fb6f257b19847249b2d72cdca3a45a47cef203ea9106ce29eeb4d9fbcccdfec7589734a8069776e8260c70d24cb26ff063758abe57d0028", KeyType.PrivateKey)));

        System.out.println("Encrypted message: " + hi);
    }

    /**
     * request缓存
     */
    private final ThreadLocal<ServletWebRequest> cache = new ThreadLocal<>();

    public EncryptionKerayHandlerMethodArgumentResolverConfig(KerayHandlerMethodArgumentResolverConfig config) {
        this.resolverConfig = config;
        this.bodyMethodProcessor = new RequestResponseBodyMethodProcessor(config.getAdapter().getMessageConverters());
        config.addKerayResolver(this);
    }

    @Override
    public int getOrder() {
        return 5;
    }

    @Override
    public boolean supportsParameter(MethodParameter parameter, NativeWebRequest webRequest, Map<Object, Object> threadLocal) {
        HttpServletRequest httpServletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
        if (httpServletRequest == null || "GET".equals(httpServletRequest.getMethod()) || httpServletRequest.getContentType() == null)
            return false;
        return httpServletRequest.getContentType().contains(MediaType.APPLICATION_JSON_VALUE) && parameter.getMethodAnnotation(EncryptionApi.class) != null;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory, Map<Object, Object> threadLocal) throws Exception {

        NativeWebRequest servlet = cache.get();
        if (servlet == null) {
            HttpServletRequest httpServletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
            if (httpServletRequest != null) {
                var servletRequest = new IHttpServletRequest(httpServletRequest);
                ServletWebRequest servletWebRequest = new ServletWebRequest(servletRequest, (HttpServletResponse) webRequest.getNativeResponse());
                Object resultObject = bodyMethodProcessor.resolveArgument(new MethodParameter(this.getClass().getMethod("methodParameter", String.class), 0), mavContainer, webRequest, binderFactory);
                if (resultObject instanceof String encryptionStr) {
                    var en = httpServletRequest.getHeader("encryption-type");
                    if ("rsa".equals(en)) {
                        try {

                            // encryptionStr = JSON.parse(encryptionStr).toString();
                            encryptionStr = rsa.decryptStr(encryptionStr, KeyType.PrivateKey);
                        } catch (Exception e) {
                            throw new BizRuntimeException(AclResultCode.illegalRequest, e);
                        }
                    } else if ("aes".equals(en)) {
                        try {
                            encryptionStr = aes.decryptStr(encryptionStr);
                        } catch (Exception e) {
                            throw new BizRuntimeException(AclResultCode.illegalRequest, e);
                        }
                    } else {
                        try {
                            encryptionStr = JSON.parse(encryptionStr).toString();
                            encryptionStr = new String(Base64.decode(sm2.decrypt(encryptionStr, KeyType.PrivateKey)));
                        } catch (Exception e) {
                            throw new BizRuntimeException(AclResultCode.illegalRequest, e);
                        }
                    }
                    servletRequest.setBody(encryptionStr.getBytes(StandardCharsets.UTF_8));
                    servlet = servletWebRequest;
                    cache.set(servletWebRequest);
                } else {
                    throw new BizRuntimeException(AclResultCode.illegalRequest);
                }
            }
        }
        if (servlet == null) {
            servlet = webRequest;
        }
        for (var resolver : resolverConfig.getKerayArgumentResolvers()) {
            if (!(resolver instanceof EncryptionKerayHandlerMethodArgumentResolverConfig) && resolver.supportsParameter(parameter, servlet, threadLocal)) {
                return resolver.resolveArgument(parameter, mavContainer, servlet, binderFactory, threadLocal);
            }
        }
        return resolverConfig.getResolverComposite().resolveArgument(parameter, mavContainer, servlet, binderFactory);
    }

    public void clear() {
        cache.remove();
    }

    public void methodParameter(String parameter) {

    }
    public static String decrypt(String encryptStr) {
        try {
            //获得密码的字节数组
            byte[] raw = "05ee4559b3db2ab9a1f5d86a316c90d4".getBytes();
            //根据密码生成AES密钥
            SecretKeySpec skey = new SecretKeySpec(raw, "AES");
            //根据指定算法ALGORITHM自成密码器
            Cipher cipher = Cipher.getInstance(ALGORITHMS);
            //初始化密码器，第一个参数为加密(ENCRYPT_MODE)或者解密(DECRYPT_MODE)操作，第二个参数为生成的AES密钥
            cipher.init(Cipher.DECRYPT_MODE, skey);
            //把密文字符串转回密文字节数组
            byte[] encodeContent = org.apache.commons.codec.binary.Base64.decodeBase64(encryptStr);
            //密码器解密数据
            byte[] byteContent = cipher.doFinal(encodeContent);
            //将解密后的数据转换为字符串返回
            return new String(byteContent, StandardCharsets.UTF_8);
        } catch (Exception e) {
            return null;
        }
    }
}

class IHttpServletRequest extends HttpServletRequestWrapper {

    private final Map<String, String[]> paramMap = new LinkedHashMap<>(32);
    private byte[] body = null;

    public IHttpServletRequest(HttpServletRequest request) {
        super(request);
    }

    @Override
    public String getParameter(String name) {
        String[] o = paramMap.get(name);
        return o == null || o.length == 0 ? null : o[0];
    }

    @Override
    public Enumeration<String> getParameterNames() {
        return Collections.enumeration(paramMap.keySet());
    }

    /**
     * 重写getParameterMap方法，原生调用HTTPRequestServlet#getParameterMap被锁定，无法调用
     * 这里重写了只对HandlerMethodArgumentResolver使用getParameterMap解析参数有效，其他情况下改装载器无法装载
     */
    @Override
    public Map<String, String[]> getParameterMap() {
        return paramMap;
    }

    @Override
    public String[] getParameterValues(String name) {
        return paramMap.get(name);
    }

    public void setBody(byte[] body) {
        this.body = body;
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (this.body == null) {
            return super.getInputStream();
        }
        return new ICoyoteInputStream(new ByteArrayInputStream(this.body), super.getInputStream());
    }


}

class ICoyoteInputStream extends ServletInputStream {

    private final ByteArrayInputStream byteArrayInputStream;

    private final ServletInputStream old;

    public ICoyoteInputStream(ByteArrayInputStream byteArrayInputStream, ServletInputStream old) {
        this.byteArrayInputStream = byteArrayInputStream;
        this.old = old;
    }

    @Override
    public boolean isFinished() {
        return old.isFinished();
    }

    @Override
    public boolean isReady() {
        return old.isReady();
    }

    @Override
    public void setReadListener(ReadListener listener) {
        old.setReadListener(listener);
    }


    @Override
    public int read(byte[] b) throws IOException {
        return byteArrayInputStream.read(b);
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        return byteArrayInputStream.read(b, off, len);
    }

    @Override
    public long skip(long n) throws IOException {
        return byteArrayInputStream.skip(n);
    }

    @Override
    public int available() throws IOException {
        return byteArrayInputStream.available();
    }

    @Override
    public void close() throws IOException {
        byteArrayInputStream.close();
    }

    @Override
    public synchronized void mark(int readlimit) {
        byteArrayInputStream.mark(readlimit);
    }

    @Override
    public synchronized void reset() throws IOException {
        byteArrayInputStream.reset();
    }

    @Override
    public boolean markSupported() {
        return byteArrayInputStream.markSupported();
    }

    @Override
    public int read() throws IOException {
        return byteArrayInputStream.read();
    }


    public static void main(String[] args) {
        try {
            // 生成RSA密钥对（确保在不同环境中使用相同的密钥对）
            RSA rsa = new RSA("RSA/ECB/PKCS1Padding", EncryptionKerayHandlerMethodArgumentResolverConfig.privateKey, EncryptionKerayHandlerMethodArgumentResolverConfig.publicKey);

            // 输出使用的密钥
            System.out.println("Private Key: " + HexUtil.encodeHexStr(rsa.getPrivateKey().getEncoded()));
            System.out.println("Public Key: " + HexUtil.encodeHexStr(rsa.getPublicKey().getEncoded()));

            // 要加密的消息
            String message = "cdn/huikao/png/202407/20240726114329095@4a16d6d198aa8bbe73ceafac72258642.png";
            System.out.println("Original message: " + message);

            // 将消息转换为字节数组并输出
            byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
            System.out.println("Message bytes: " + HexUtil.encodeHexStr(messageBytes));

            // 使用私钥加密
            byte[] encryptedMessage = rsa.encrypt(messageBytes, KeyType.PrivateKey);
            System.out.println("Encrypted message (hex): " + HexUtil.encodeHexStr(encryptedMessage));

            // 使用公钥解密
            byte[] decryptedMessage = rsa.decrypt(encryptedMessage, KeyType.PublicKey);
            String decryptedMessageStr = new String(decryptedMessage, StandardCharsets.UTF_8);
            System.out.println("Decrypted message: " + decryptedMessageStr);

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public static String decryptAES(String str) {
        AES aes = new AES("".getBytes());
        return aes.decryptStr(str);
        // 自动 Base64 解码并解密
    }


}

